/********************************************************************/
/*          loader_main.c                                           */
/*            Loader Main                                           */
/*                                                                  */
/*          Copyright (C) 2001 NINTENDO Co.,Ltd.                    */
/********************************************************************/
//#define CODE32
#include "sio32_multi_load.h"

//#define KEY_ON      // Flag to enable OBJ key operation

#define INITIAL_CODE    (*(vu32 *)"AGBJ")   // Initial Code


/*-------------------- Global Variable  ----------------------------*/

extern u8 *LoadTable[4];                    // Load Table


/*==================================================================*/
/*                      Main Routine                                */
/*==================================================================*/
extern void intr_main(void);
u8     *LoadCounterp;
u8      RecvLoadCounter;
u8      BurstLoad;
u32     ProgressCounter;

void LoaderMain(void)
{
    u32    MultiSioId;
    u8    *Sio32Datap = 0;
    u8     LoadStartFlag;
    char  *StrTmp;
    s32    i;

    RegisterRamReset(RESET_CPU_WRAM_FLAG                   // Initialization
                   | RESET_ALL_REG_FLAG ^ RESET_REG_SIO_FLAG);

    DmaCopy(3, IntrTable, IntrTableBuf,sizeof(IntrTableBuf),32);// Set Interrupt Table
    DmaCopy(3, intr_main, IntrMainBuf, sizeof(IntrMainBuf), 32);// Set Interrupt Main Routine
    *(vu32 *)INTR_VECTOR_BUF = (vu32 )IntrMainBuf;

    DmaArrayCopy(3, CharData_Sample, BG_VRAM+0x8000, 32);  //  Set BG Character
    DmaArrayCopy(3, CharData_Sample, OBJ_MODE0_VRAM, 32);  // Set OBJ Character
    DmaArrayCopy(3, PlttData_Sample, BG_PLTT,        32);  //  Set BG Palette
    DmaArrayCopy(3, PlttData_Sample, OBJ_PLTT,       32);  // Set OBJ Palette

    DmaArrayCopy(3, BgScData_Sample, BgBak,          32);  // Set BG Screen
    DmaArrayCopy(3, BgBak,           BG_VRAM,        32);

    DmaArrayClear(3,160,             OamBak,         32);  // Undisplayed OBJ moves outside screen
    DmaClear(     3,160,             OAM, OAM_SIZE,  32);
    DmaArrayCopy(3, OamData_Sample,  OamBak,         32);  // Set OAM
//    DmaArrayCopy(3, OamBak,          OAM,            32);

    for (i=0; i<MULTI_SIO_PLAYERS_MAX; i++)     OamBak[i].VPos = 200;

    *(vu16 *)REG_BG0CNT =                                  // Set BG Control
            BG_COLOR_16 | BG_SCREEN_SIZE_0 | BG_PRIORITY_0
            | 0 << BG_SCREEN_BASE_SHIFT | 2 << BG_CHAR_BASE_SHIFT ;

    *(vu16 *)REG_IE    = V_BLANK_INTR_FLAG;                // Enable V-Blank Interrupt
    if (*(vu8  *)(ROM_BANK0+0xb2) == 0x96                  // Check Game Pak Inserted
     && *(vu32 *)(ROM_BANK0+0xac) == INITIAL_CODE)
        *(vu16 *)REG_IE |= CASSETTE_INTR_FLAG;             // Enbale Game Pak Interrupt
    *(vu16 *)REG_STAT  = STAT_V_BLANK_IF_ENABLE;
    *(vu16 *)REG_IME   = 1;                                // Set IME

    *(vu16 *)REG_DISPCNT = DISP_MODE_0 | DISP_BG0_ON | DISP_OBJ_ON; // LCDC ON

    LoadCounterp = &((CommData *)UsrSendBuf)->LoadCounter;

    while(1) {
        if (!(SioFlags & MULTI_SIO_CONNECTED_ID_MASK)   // Master or prior to connection determination
          || (SioFlags & MULTI_SIO_PARENT)
          || (SioState >= 4))
                VBlankIntrWait();                       // Wait for end V-Blank interrupt
        else    IntrWait(1, SIO_INTR_FLAG);             // Wait for end SIO Interrupt

        if (Trg) {   BurstLoad ^= 1;
//                  *LoadCounterp = 0;
        }

        switch (SioState) {
            case 0: UsrSendBuf[0] = 200;            // Initialize OBJ Display Location
                    SioFlagsBak = 0;
            case 1:
#ifdef MULTI_SIO_DI_FUNC_FAST
                    IntrTableBuf[0] = (IntrFuncp )((u32 )IntrFuncBuf + 1);// Set Interrupt Address
#else
                    IntrTableBuf[0] = MultiSioIntr;
#endif
                    MultiSioInit((SioFlagsBak & MULTI_SIO_CONNECTED_ID_MASK) >> 8);
                                                                    // Multi-play Communication Intitialization
                    LoadStartFlag = 0;
                    if (SioState == 0)      SioState = 3;
                    else                    SioState = 2;
                    break;
            case 2: if (SioFlags & MULTI_SIO_LD_ENABLE) {           // Update Load Counter
                        if (SioFlags & MULTI_SIO_LD_SUCCESS) {
                            if (++(*LoadCounterp) >= sizeof(LoadTable)/4) {
//                               *LoadCounterp = 0;
//                                BurstLoad = 0;
                            }
                        }
                        SioState = 3;
                    }
                    MultiSioStart();                                // Multi-play Communication Start from 2nd time on
            case 3: if (SioFlags & MULTI_SIO_LD_REQUEST) {          // To be safe, resend after confirmation
                        if (!(SioFlags & MULTI_SIO_PARENT))
                                SioState = 4;
                        else if (RecvLoadCounter < sizeof(LoadTable)/4
                                || Trg)                             // Button start with master!!
                                SioState = 4;
                    }
                    if (BurstLoad)
                        if ((SioFlags & MULTI_SIO_LD_ENABLE))
                            LoadStartFlag = 1;
                    SioFlags = MultiSioMain(UsrSendBuf, UsrRecvBuf, // Multi-play Communication Main
                                            LoadStartFlag);
                    if (Trg)   MultiSioStart();                     // Start Multi-play Communication

                    if (RecvLoadCounter != ((CommData *)UsrRecvBuf[0])->LoadCounter) {
                        RecvLoadCounter =  ((CommData *)UsrRecvBuf[0])->LoadCounter;
                        ProgressCounter = 0;
                    }
                    Sio32Datap = LoadTable[RecvLoadCounter];
                    break;
            case 4: if (RecvLoadCounter == sizeof(LoadTable)/4) {
                        RegisterRamReset(RESET_CPU_WRAM_FLAG        // Initialization
                                       | RESET_ALL_REG_FLAG ^ RESET_REG_SIO_FLAG);
                        ((VoidFuncp )0x02000000)();                 // Execute Download Program
                    }

                    *(vu16 *)REG_IME = 0;
                    *(vu16 *)REG_IE &= ~(SIO_INTR_FLAG              // Disable SIO & Timer Interrupt
                                       | MULTI_SIO_TIMER_INTR_FLAG);
                    *(vu16 *)REG_IME = 1;
#ifdef SIO32ML_DI_FUNC_FAST                                         // Set Interrupt Address
                    IntrTableBuf[0] = (IntrFuncp )((u32 )IntrFuncBuf + 1);
#else
                    IntrTableBuf[0] = Sio32MultiLoadIntr;
#endif
                    Sio32MultiLoadInit((SioFlags & MULTI_SIO_PARENT),// 32bit Serial Communication Multi-load Initialization
                                        Sio32Datap);
                    SioState = 5;
                    break;
            case 5: if (Sio32MultiLoadMain(&ProgressCounter)) {     // 32bit Serial Communication Multi-load Main
                        SioFlagsBak = SioFlags;
                        SioFlags = 0;
                        SioState = 1;
                    }
                    break;
        }


        BgScSet("0x", &BgBak[32*16+7], 3);              // Communication End Data Size Display
        Data2BgSc(RecvLoadCounter * SIO32ML_BLOCK_SIZE + ProgressCounter * 4,
                  &BgBak[32*16+9], 8, 3);

        if (SioFlags & MULTI_SIO_PARENT) {
            if (*LoadCounterp == sizeof(LoadTable)/4)
                                  StrTmp = "PUSH ANY KEY  ";
            else if (BurstLoad)   StrTmp = "NOW LOADING...";
            else                  StrTmp = "PUSH ANY KEY  ";
            BgScSet(StrTmp, &BgBak[32*18+7], 1);
        }


        if (!SioStartFlag) {                            // After connect recognized
            if (SioFlags & 0xf) {                       // initialize OBJ location
                MultiSioId = ((SioMultiCnt *)REG_SIOCNT)->ID;
                UsrSendBuf[0] = ((OamData *)OamData_Sample)[MultiSioId].VPos;
                UsrSendBuf[1] = ((OamData *)OamData_Sample)[MultiSioId].HPos;
                SioStartFlag = 1;
            }
        }

        for (i=0; i<4; i++)
            if (SioFlags & (1 << i)) {
                OamBak[i].VPos = UsrRecvBuf[i][0];
                OamBak[i].HPos = UsrRecvBuf[i][1];
            }


        KeyRead();                                      // Key Operation
    }
}


